//  Copyright 2011 Charlie Garrett
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
//  CodeGenerator.h
//

#ifndef CODEGENERATOR_H
#define CODEGENERATOR_H

#include <map>
#include <utility>

#include "logic.h"
#include "Instruction.h"
#include "Interpreter.h"
#include "Term.h"

namespace logic {
    using namespace std;
        
    // Code generation state.
    class CodeGenerator {
    private:
        Interpreter *interpreter;
        
        // TODO: Make these triplets of register fields into arrays of the relevant type.
        unsigned next_temp_reg;
        unsigned next_permanent_reg;
        unsigned next_arg_reg;
        
        // Temporary, permanent and argument registers allocated to terms.
        map<Term*, Register> temp_regs;
        map<Term*, Register> permanent_regs;
        map<Term*, Register> arg_regs;
        
        // Terms available in each register at the current code generation point.
        map<Register, Term*> temp_available_term;
        map<Register, Term*> permanent_available_term;
        map<Register, Term*> arg_available_term;

        // Map from rule head Functor and clause number to entry point offsets.
        map<Instruction::Label, size_t> entry_points;
        bool valid_label;
        Instruction::Label next_instruction_label;

    public:
        CodeGenerator(Interpreter *intrprtr) : interpreter(intrprtr), next_temp_reg(0), 
        next_permanent_reg(0), next_arg_reg(0), valid_label(false), next_instruction_label(Functor::Undefined, 0) {}

        void InitState();
        void ClearRegisters();
        void ClearTempRegisters();
        void ClearPermanentRegisters();
        void ClearArgRegisters();

        // Allocate a register for the term if it doesn't have one already.
        Register AllocateRegister(Register::Kind kind, Term *term);
        bool IsRegisterUsed(Register reg);
        Register GetRegister(Register::Kind kind, Term *term);
        
        unsigned NumPermanentVars() { return next_permanent_reg; }
        
        void EmitInstruction(Instruction *inst, Term *term = NULL);
        
        Instruction::Label LabelInstruction(const Functor &functor,
                                            size_t clause_num = 0);
        bool LabelOffset(const Instruction::Label &key, size_t *offset);
        bool ResolveLabels();
        
        // Remove all instructions from the given label to the next
        // label in the code.  Remove the label itself from entry_points.
        void RemoveInstructions(Instruction::Label start);
    };
}  // namespace logic

#endif  // CODEGENERATOR_H
